/*
 *  Copyright (C) 2010 Matthias Buch-Kromann <mbk.isv@cbs.dk>
 * 
 *  This file is part of the IncrementalParser package.
 *  
 *  The IncrementalParser program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 * 
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.osdtsystem.incparser.logging;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 *
 * @author Matthias Buch-Kromann <mbk.isv@cbs.dk>
 */
public class HtmlDirectoryParserHandler extends ParserHandler {
    static String UNNAMED = "?";
    String directory;
    String mainPrefix = "index";
    String iterationPrefix = "iteration";
    String sentencePrefix = "sentence";
    String statePrefix = "state";
    String parseTreePrefix = "tree";
    String extension = ".html";
    Writer mainWriter = null;
    Writer iterationWriter = null;
    Writer sentenceWriter = null;
    Writer stateWriter = null;
    Writer treeWriter = null;
    Formatter formatter = null;
    Map<String, Integer> states2ids = new HashMap<String, Integer>();
    List<List<Integer>> substates = new ArrayList<List<Integer>>();
    List<Integer> superstates = new ArrayList<Integer>();
    List<String> stateNames = new ArrayList<String>(),
            sentenceNames = new ArrayList<String>(),
            iterationNames = new ArrayList<String>();
    Set<Integer> finalStates = new TreeSet<Integer>();
    Set<Integer> doneStates = new HashSet<Integer>();
    int iteration = 0, sentence = 0, state = 0, nextState = 0;

    public HtmlDirectoryParserHandler(Formatter formatter) throws IOException, SecurityException {
        this(formatter, "parser.out/");
    }

    public HtmlDirectoryParserHandler(Formatter formatter, String directory) throws IOException, SecurityException {
        this.formatter = formatter;
        this.directory = directory;
        // Delete directory
        File dir = new File(directory);
        rmdir(dir);
        dir.mkdir();
    }

    String mainFileName() {
        return (mainPrefix + extension);
    }

    String iterationFileName(int iteration) {
        return (iterationPrefix + "-" + iteration + extension);
    }

    String sentenceFileName(int iteration, int sentence) {
        return (sentencePrefix + "-" + iteration + "-" + sentence + extension);
    }

    String stateFileName(int iteration, int sentence, int state) {
        return (statePrefix + "-" + iteration + "-" + sentence + "-" + state + extension);
    }

    @Override
    public void publish(LogRecord record) {
        if (this.getLevel().intValue() <= record.getLevel().intValue()) {
            try {
                if (stateWriter != null) {
                    stateWriter.write(formatter.format(record));
                } else if (sentenceWriter != null) {
                    sentenceWriter.write(formatter.format(record));
                } else if (iterationWriter != null) {
                    iterationWriter.write(formatter.format(record));
                } else {
                    mainWriter.write(formatter.format(record));
                }
            } catch (IOException ex) {
                Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    @Override
    public void flush() {
        Writer[] writers = {stateWriter, sentenceWriter, iterationWriter, mainWriter, treeWriter};
        for (Writer writer : writers) {
            if (writer != null) {
                try {
                    writer.flush();
                } catch (IOException ex) {
                    Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }

    @Override
    public void close() {
        closeMain();
    }

    @Override
    public void openMain() {
        closeMain();
        mainWriter = new StringWriter();
        try {
            mainWriter.write("ParserHandler iteration=" + iteration + " sentence=" + sentence + " state=" + state + "\n");
        } catch (IOException ex) {
            Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void openIteration() {
        closeIteration();
        iterationNames.add(UNNAMED);
        iterationWriter = new StringWriter();
        try {
            iterationWriter.write("ParserHandler iteration=" + iteration + " sentence=" + sentence + " state=" + state + "\n");
        } catch (IOException ex) {
            Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void openSentence()  {
        closeSentence();
        sentenceNames.add(UNNAMED);
        sentenceWriter = new StringWriter();
        try {
            sentenceWriter.write("ParserHandler iteration=" + iteration + " sentence=" + sentence + " state=" + state + "\n");
        } catch (IOException ex) {
            Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void openState(String stateName) {
        closeState();
        state = state(stateName);
        doneStates.add(state);
        stateWriter = new StringWriter();
    try {
            stateWriter.write("ParserHandler iteration=" + iteration + " sentence=" + sentence + " state=" + state + "\n");
        } catch (IOException ex) {
            Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @Override
    public void openStateFinal(String stateName) {
        openState(stateName);
        finalStates.add(state(stateName));
    }

    @Override
    public void closeMain()  {
        closeIteration();
        if (mainWriter != null) {
            HTMLWriter html = null;
            try {
                mainWriter.write("ParserHandler iterations=" + iteration
                        + " sentences=" + sentence + " states=" + state);

                // Print main heading
                String mainFN = mainFileName();
                html = new HTMLWriter(directory, mainFileName());
                html.writeTag("h1", "Main (" + mainFN + ")");
                html.nl();

                // Print main log
                html.nl();
                html.writeTag("h2", "Main log");
                html.nl();
                html.openTag("pre");
                html.nl();
                html.write(mainWriter.toString());
                html.nl();
                html.closeTag("pre");
                
                // Print iterations
                html.writeTag("h2", "Iterations");
                html.nl();
                html.openTag("ul");
                for (int i = 0; i < iteration; ++i) {
                    html.write("    ");
                    html.openTag("li");
                    html.url(iterationFileName(i), "" + i);
                    html.write(": ");
                    html.write(getIterationName(i));
                    html.closeTag("li");
                    html.nl();
                }
                html.closeTag("ul");

                
                // Close main writer
                html.close();
                mainWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                mainWriter = null;
                iterationNames.clear();
                iteration = 0;
            }
        }
    }

    @Override
    public void closeIteration()  {
        closeSentence();
        if (iterationWriter != null) {
            HTMLWriter html = null;
            try {
                iterationWriter.write("ParserHandler sentences=" + sentence + " states=" + state);

                // Print iteration heading
                String iterationFN = iterationFileName(iteration);
                html = new HTMLWriter(directory, iterationFN);
                html.writeTag("h1", "Iteration " + iteration + ": " + getIterationName(iteration) + " (" + iterationFN + ")");

                // Navigation
                html.url(mainFileName(), "Main");
                html.br();
                html.writeTag("b", "Previous: ");
                if (iteration > 0) {
                    html.url(iterationFileName(iteration - 1), "iteration");
                }
                html.br();
                html.writeTag("b", "Next: ");
                html.url(iterationFileName(iteration + 1), "iteration");
                html.nl();
                html.nl();

                // Print iteration log
                html.writeTag("h2", "Iteration log");
                html.nl();
                html.openTag("pre");
                html.write(iterationWriter.toString());
                html.nl();
                html.closeTag("pre");
                html.nl();
                html.nl();

                // Print sentences with scores and links
                html.writeTag("h2", "Sentences");
                html.openTag("ul");
                for (int i = 0; i < sentence; ++i) {
                    html.write("    ");
                    html.openTag("li");
                    html.url(sentenceFileName(iteration, i), "" + i);
                    html.write(": ");
                    html.write(getSentenceName(i));
                    html.closeTag("li");
                    html.nl();
                }
                html.closeTag("ul");
                html.nl();

                // Close iteration writer
                html.close();
                iterationWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
                return;
            } finally {
                iterationWriter = null;
                sentence = 0;
                sentenceNames.clear();
                ++iteration;
            }
        }
    }

    @Override
    public void closeSentence() {
        closeState();
        if (sentenceWriter != null) {
            try {
                sentenceWriter.write("ParserHandler states=" + state);

                // Print iteration heading
                String sentenceFN = sentenceFileName(iteration, sentence);
                HTMLWriter html = new HTMLWriter(directory, sentenceFN);
                html.writeTag("h1", "Sentence " + sentence + ": " + getSentenceName(sentence) + " (" + sentenceFN + ")");

                // Navigation
                html.url(mainFileName(), "Main");
                html.br();
                html.url(iterationFileName(iteration), "Iteration " + iteration);
                html.write(": " + getIterationName(iteration));
                html.br();
                html.writeTag("b", "Previous: ");
                if (iteration > 0) {
                    html.url(sentenceFileName(iteration - 1, sentence), "iteration");
                    html.write(" ");
                }
                if (sentence > 0) {
                    html.url(sentenceFileName(iteration, sentence - 1), "sentence");
                }
                html.br();
                html.writeTag("b", "Next: ");
                html.url(sentenceFileName(iteration + 1, sentence), "iteration");
                html.write(" ");
                html.url(sentenceFileName(iteration, sentence + 1), "sentence");
                html.br();

                // Print sentence log
                html.writeTag("h2", "Sentence log");
                html.nl();
                html.openTag("pre");
                html.write(sentenceWriter.toString());
                html.nl();
                html.closeTag("pre");
                html.nl();
                html.nl();

                // Print final states
                html.writeTag("h2", "Final states");
                for (int i : finalStates) {
                    html.url(stateFileName(iteration, sentence, i), "" + i);
                    html.write(": ");
                    html.write(getStateName(i));
                    html.br();
                    html.nl();
                }

                // Print parse tree
                html.writeTag("h2", "States (parse tree)");
                printParseTree(html, 0, 0);
                html.nl();

                // Print states in processing order
                html.writeTag("h2", "States (processing order)");
                html.openTag("ul");
                for (int i = 0; i < state; ++i) {
                    if (doneStates.contains(i)) {
                        html.url(stateFileName(iteration, sentence, i), "" + i);
                        html.write(": ");
                        html.write(getStateName(i));
                        html.br();
                        html.nl();
                    }
                }

                // Close iteration writer
                html.close();
                sentenceWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                try {
                    sentenceWriter.close();
                } catch (IOException ex) {
                    Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    states2ids.clear();
                    sentenceWriter = null;
                    substates.clear();
                    superstates.clear();
                    stateNames.clear();
                    finalStates.clear();
                    state = nextState = 0;
                    ++sentence;
                    doneStates.clear();
                    finalStates.clear();
                }
            }
        }
    }


    @Override
    public void closeState()  {
        if (stateWriter != null) {
            HTMLWriter html = null;
            try {
                int thisState = this.state;
                // Print iteration heading
                String stateFN = stateFileName(iteration, sentence, thisState);
                html = new HTMLWriter(directory, stateFN);
                html.writeTag("h1", "State " + thisState + ": " + getStateName(thisState) + " (" + stateFN + ")");

                // Navigation
                html.url(mainFileName(), "Main");
                html.br();
                html.url(iterationFileName(iteration), "Iteration " + iteration);
                html.write(": " + getIterationName(iteration));
                html.br();
                html.url(sentenceFileName(iteration, sentence), "Sentence " + sentence);
                html.write(": " + getSentenceName(sentence));
                html.br();
                html.writeTag("b", "States: ");
                int parent = getSuperstate(thisState);
                if (parent >= 0) {
                    html.url(stateFileName(iteration, sentence, parent), "parent");
                    html.write(" ");
                }

                // Print parse tree
                html.writeTag("h2", "Child states");
                html.nl();
                for (int i : getSubstates(thisState)) {
                    html.url(stateFileName(iteration, sentence, i), "" + i);
                    html.write(": ");
                    html.write(getStateName(i));
                    html.br();
                }

                // Print state log
                html.writeTag("h2", "State log");
                html.nl();
                html.openTag("pre");
                html.write(stateWriter.toString());
                html.nl();
                html.closeTag("pre");
                html.nl();
                html.nl();

                // Close iteration writer
                html.close();
                stateWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(HtmlDirectoryParserHandler.class.getName()).log(Level.SEVERE, null, ex);
            } finally {
                stateWriter = null;
                ++state;
            }
        }
    }

    public void printParseTree(HTMLWriter writer, int state, int depth)  {
        if (state >= 0 && state < stateNames.size()) {
            if (! doneStates.contains(state))
                return;

            // Print state
            for (int indent = 0; indent < depth; ++indent) {
                writer.write("&nbsp;&nbsp;");
            }
            writer.url(stateFileName(iteration, sentence, state), "" + state);
            writer.write(": ");
            writer.write(getStateName(state));
            writer.openTag("br");
            writer.nl();

            // Print substates
            if (state <= substates.size()) {
                for (int substate : getSubstates(state)) {
                    printParseTree(writer, substate, depth + 1);
                }
            }
        }
    }

    @Override
    public void setIterationName(String name) {
        iterationNames.set(iteration, name);
    }

    @Override
    public void setSentenceName(String name) {
        while (sentenceNames.size() <= sentence)
            sentenceNames.add(UNNAMED);
        sentenceNames.set(sentence, name);
    }

    public void setStateName(String name) {
        stateNames.set(state, name);
        states2ids.put(name, state);
    }

    public String getIterationName(int iteration) {
        return iterationNames.get(iteration);
    }

    public String getSentenceName(int sentence) {
        return sentenceNames.get(sentence);
    }

    public String getStateName(int state) {
        if (state >= 0 && state < stateNames.size()) 
            return stateNames.get(state);
        else
            return UNNAMED;
    }


    public int state(String stateName) {
        if (states2ids.containsKey(stateName)) {
            return states2ids.get(stateName);
        } else {
            states2ids.put(stateName, nextState);
            while (nextState + 1 >= stateNames.size()) {
                substates.add(new ArrayList<Integer>());
                stateNames.add(UNNAMED);
                superstates.add(-1);
            }
            stateNames.set(nextState, stateName);
            return nextState++;
        }
    }

    @Override
    public void addSubstate(String parentStateName, String childStateName) {
        int parentState = state(parentStateName);
        int childState = state(childStateName);
        superstates.set(childState, parentState);
        substates.get(parentState).add(childState);
    }

    public List<Integer> getSubstates(int state) {
        return substates.get(state);
    }

    public int getSuperstate(int state) {
        return superstates.get(state);
    }

    public static boolean rmdir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = rmdir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        return dir.delete();
    }
}